﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace LevelEditor
{
    public class Camera
    {
        public static Camera instance = new Camera();

        public Vector3 lookat
        {
            get { return lookat_main; }
        }

        private Vector3 lookat_main;
        private Vector3 lookat_left;
        public Vector3 position;

        /// <summary>
        /// Used to rotate the camera per one frame
        /// </summary>
        public Vector2 move;
        
        public float grav;

        private float rotation_u, rotation_v;

        public float AspectRatio;

        public Matrix View
        {
            get { return Matrix.CreateLookAt(position, lookat, Vector3.Up); }
        }

        public Matrix Projection
        {
            get { return Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(90.0f), AspectRatio, 0.1f, 10000.0f); }
        }

        public Matrix World
        {
            get { return Matrix.CreateTranslation(position); }
        }

        public Camera()
        {
            position = Vector3.Backward * 4.0f + Vector3.Up;
        }

        public Vector3 Forward
        {
            get { return lookat - position; }
        }

        public Vector3 Left
        {
            get { return lookat_left - position; }
        }

        public void FormatEffect(Effect effect)
        {
            effect.Parameters["World"].SetValue(Matrix.Identity);
            effect.Parameters["Projection"].SetValue(Projection);
            effect.Parameters["View"].SetValue(View);
        }

        public void Update(float dt)
        {
            rotation_u -= MathHelper.ToRadians(move.X / 10.0f);
            rotation_v += MathHelper.ToRadians(move.Y / 10.0f);

            Quaternion q = Quaternion.CreateFromAxisAngle(Vector3.Up, rotation_u) * Quaternion.CreateFromAxisAngle(Vector3.Left, rotation_v);

            lookat_main = position + Vector3.Transform(Vector3.Forward, Matrix.CreateFromQuaternion(q));
            lookat_left = position + Vector3.Transform(Vector3.Left, Matrix.CreateFromQuaternion(q));
            move = Vector2.Zero;
        }

        /// <summary>
        /// Computes a ray that is the provided position on screen, to a direction in the world
        /// </summary>
        /// <param name="viewport">The viewport to use to project</param>
        /// <param name="x">the x coordinate of the position to project</param>
        /// <param name="y">the y coordinate of the position to project</param>
        /// <returns>A ray projected into the world</returns>
        public Ray ComputeRay(Viewport viewport, float x, float y)
        {
            Vector3 near = viewport.Unproject(new Vector3(x, y, 0), Projection, View, Matrix.Identity);
            Vector3 far = viewport.Unproject(new Vector3(x, y, 1), Projection, View, Matrix.Identity);

            Vector3 direction = far - near;
            direction.Normalize();

            return new Ray(near, direction);
        }
        

        public Matrix Local
        {
            get
            {
                Vector3 forward = Forward;
                Vector3 left = Left;
                forward.Normalize();
                left.Normalize();
                Vector3 up = Vector3.Cross(left, forward);
                up.Normalize();
                return new Matrix(
                    Forward.X, Forward.Y, Forward.Z, 0.0f,
                    up.X, up.Y, up.Z, 0.0f,
                    Left.X, Left.Y, Left.Z, 0.0f,
                    0, 0, 0, 1.0f);
            }
        }
    }
}